/*
 * mythread.h -- interface of user threads library
 */

#ifndef MYTHREAD_H
#define MYTHREAD_H

#ifndef _REENTRANT
#define _REENTRANT
#endif

#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE 1
#endif

#ifndef ZERO
#define ZERO 0
#endif

#include <ucontext.h>
#include <unistd.h>
#include <setjmp.h>
#include <pthread.h>
#include <errno.h>
#include "myqueue.h"
#include <semaphore.h>
#include <sched.h>

typedef struct mypsem {
  pthread_mutex_t lock;
  pthread_cond_t cond;
  int count;
} mypsem_t;

typedef struct mythread_attr {        /* thread attributes */
  struct sched_param param;
} mythread_attr_t;

/*
 * state of a thread
 */
#define MYTHREAD_RUN  0x0001
#define MYTHREAD_COND 0x0002
#define MYTHREAD_EXIT 0x0004
#define MYTHREAD_SUSP 0x0008
#define MYTHREAD_JOIN 0x0010

typedef struct mythread {             /* thread control block */
  int state;                          /* state of execution */
  void * (*start_func)(void*);        /* thread_func to be called */
  void *arg;                          /* thread_arg arguments of thread_func */
  mythread_queue_t joinq;             /* Q of threads waiting for my return */
  ucontext_t context;                 /* save area for context switches */
  mythread_attr_t attr;               /* thread attributes (base priority) */
  int dprio;                          /* dyn./effective prio (for scheduling) */
  pthread_t lwpid;                    /* light weight process ID */
  mypsem_t lwpsem;                    /* light weight process semaphore */
  int reschedule;                     /* if TRUE then reschedule ASAP */
  mypsem_t block;                     /* semaphore to suspend/resume exec. */
} *mythread_t;

/*
 * mythread_readyq - pointer to ready q
 */
mythread_queue_t *mythread_readyq(void);

/*
 * mythread_runq - pointer to run q
 */
mythread_queue_t *mythread_runq(void);

/*
 * mythread_setconcurrency - set the number of LWPs
 * (max. number of parallel threads)
 */
void mythread_setconcurrency(int new_level);

/*
 * mythread_enter_kernel - enter the monolithic threading kernel
 */
void mythread_enter_kernel(void);

/*
 * mythread_tryenter_kernel - enter the monolithic threading kernel IF not busy
 * return TRUE on success, FALSE o/w
 */
int mythread_tryenter_kernel(void);

/*
 * mythread_leave_kernel_nonpreemptive - leave the monolithic threading kernel
 * without allowing preemptions
 */
void mythread_leave_kernel_nonpreemptive(void);

/*
 * mythread_leave_kernel - leave the monolithic threading kernel w/ preemptions
 */
void mythread_leave_kernel(void);

/*
 * mythread_self - thread id of running thread
 */
mythread_t mythread_self(void);

/*
 * mythread_block - remove currently running thread off run q,
 * if q is non-NULL, enq this thread on q, suspend the thread,
 * add the state flags to the thread's state (via bit-wise OR | state)
 * and activate ready threads if an LWP becomes available
 * pre: must be in monolithic kernel
 * post: return outside the monolithic kernel
 */
void mythread_block(mythread_queue_t *q, int state);

/*
 * mythread_block_thread - block thread th if readyq is non-empty
 * (o/w do nothing). This involves a deq from runq and an enq on q
 * as well as adding state (OR |) to the thread's state
 * pre: must be in monolithic kernel
 * post: still in monolithic kernel
 */
void mythread_block_thread(mythread_t th, mythread_queue_t *q, int state);

/*
 * mythread_unblock - resumes the thread at the head of q,
 * remove the state flags to the thread's state (via bit-wise AND & ~state)
 * and activate the thread if an LWP becomes available (o/w mark as ready)
 * pre: must be in monolithic kernel
 * post: return outside the monolithic kernel with dprio reset to base prio
 */
void mythread_unblock(mythread_queue_t *q, int state);

/*
 * mythread_create - prepares context of new_thread_ID as start_func(arg),
 * attr: if NULL, priority is 10 else attr is copied into the TCB attr 
 * Threads are activated (run) according to the number of available LWPs
 * or are marked as ready.
 */
int mythread_create(mythread_t *new_thread_ID,
		    const mythread_attr_t *attr,
		    void * (*start_func)(void *),
		    void *arg);

/*
 * mythread_yield - switch from running thread to the next ready one
 */
int mythread_yield(void);

/*
 * mythread_join - suspend calling thread if target_thread has not finished,
 * enqueue on the join Q of the target thread, then dispatch ready thread;
 * once target_thread finishes, it activates the calling thread / marks it
 * as ready.
 */
int mythread_join(mythread_t target_thread, void **status);

/*
 * mythread_exit - exit thread, awakes joiners on return
 * from thread_func and dequeue itself from run Q before dispatching run->next
 */
void mythread_exit(void *retval);

/*
 * mythread_init_sched - called once at the start of each thread
 * enable virtual timer signals, install signal handler for it and
 * set time slice to 20ms for virtual timer
 */
void mythread_init_sched(void);

/*
 * mythread_exit_sched - called on thread terminiation (exit) by each thread
 * disable all the features activated in mythread_init_sched()
 */
void mythread_exit_sched(void);

/* 
 * mythread_attr_setschedparam - set the value of scheduling
 * parameters ('sched_priority') in the attribute,
 * to define/change the thread priority, 
 * see /usr/include/linux/sched.h for struct sched_param
 */
int mythread_attr_setschedparam (mythread_attr_t *attr, 
				 struct sched_param *param);

/* 
 * mythread_attr_getschedparam - return the value of the schedling
 * parameters ('sched_priority') from the attribute
 */
int mythread_attr_getschedparam (const mythread_attr_t *attr, 
				 struct sched_param *param);

#endif /* MYTHREAD_H */
